package org.vacoor.xqq.ui.chat;

import org.vacoor.xqq.core.msg.EventBusHolder;
import org.vacoor.xqq.core.msg.chatmsg.ContactMsg;
import org.vacoor.xqq.core.msg.chatmsg.MsgBody;
import org.vacoor.xqq.core.msg.chatmsg.MsgElement;
import org.vacoor.xqq.core.msg.chatmsg.MsgStyle;
import org.vacoor.xqq.core.msg.req.ReplyMsg;
import org.vacoor.xqq.ui.comp.richeditor.chooser.FaceInputMonitor;
import org.vacoor.xqq.ui.dynamictree.Buddy;
import org.vacoor.xqq.ui.comp.richeditor.*;
import org.vacoor.xqq.ui.comp.richeditor.chooser.FaceChooser;
import org.vacoor.xqq.ui.comp.richeditor.chooser.FaceUtil;
import org.vacoor.xqq.ui.comp.richeditor.chooser.StyleChooser;
import org.vacoor.xqq.ui.util.WindowMoveHandler;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * User: vacoor
 */
public class UIChat extends JPanel implements Chat {
    private static final int FONT_OFFSET = 2;  // 总是比系统字号小, 稍微矫正下
    private static final Color DEFAULT_TITLE_PANEL_BACKGROUND = new Color(0xE0EFF6);
    private static final Color DEFAULT_MID_TOOLBAR_BACKGROUND = new Color(0xE0EFF6);
    private static final Color DEFAULT_CONTENT_BORDER_COLOR = new Color(0xD4D6D3);
    private static final String DEFAULT_HELP_TEXT = "";

    private static final ImageIcon styleButtonIcon = new ImageIcon(UIChat.class.getClassLoader().getResource("images/MidToolbar/aio_quickbar_font.png"));
    private static final ImageIcon faceButtonIcon = new ImageIcon(UIChat.class.getClassLoader().getResource("images/MidToolbar/aio_quickbar_face.png"));

    private AvatarPanel avatarPanel;

    private JSplitPane contentPane;
    private RichEditor outputEditor;
    private RichEditor inputEditor;

    private JToggleButton styleBtn;
    private JToggleButton faceBtn;

    private StyleChooser styleChooser;
    private FaceChooser faceChooser;

    private String helpText = DEFAULT_HELP_TEXT;
    private JButton helpBtn;

    private Handler handler;

    private final Buddy buddy;

    UIChat(Buddy buddy) {
        this.buddy = buddy;

        init();
        avatarPanel.initDataAndRenderer(buddy);

        /*
        int i = (int) (Math.random() * 10);
        Icon icon = new ImageIcon(
                ChatPanel.class.getClassLoader().getResource("images/00" + i + ".png")
        );
        avatarPanel.setAvatarIcon(icon);
        */

        new FaceInputMonitor(inputEditor.getTextPane());
        new WindowMoveHandler().bindWindowMoveHanderTo(avatarPanel);
    }

    private void init() {
        this.setLayout(new BorderLayout(0, 0));

        // 顶部标题栏
        avatarPanel = new AvatarPanel();
        avatarPanel.setOpaque(true);
        avatarPanel.setBackground(DEFAULT_TITLE_PANEL_BACKGROUND);
        avatarPanel.setBorder(BorderFactory.createMatteBorder(5, 10, 4, 0, new Color(255, 255, 255, 0)));

        // 内容区域
        contentPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        /*-
         * splitPane.setDividerLocation(0.6) 只在组件可见时有效
         * 添加 ComponentListener 可见时不会派发show事件
         * (因为JComponent visible 默认为true, 调用setVisible()才会)
         * 但是显示时候会派发 resize
         * 这里使用 setResizeWeight
         */
        contentPane.setResizeWeight(0.65);
        contentPane.setOneTouchExpandable(false);
        contentPane.setDividerSize(1);
        contentPane.setOpaque(false);
        contentPane.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, DEFAULT_CONTENT_BORDER_COLOR));

        // 内容区域输出
        final JPanel outPanel = new JPanel(new BorderLayout(0, 0));
        outPanel.setOpaque(false);

        outputEditor = new RichEditor();
        outputEditor.setEditable(false);

        styleChooser = new StyleChooser();
        styleChooser.setVisible(false);

        outPanel.add(outputEditor, BorderLayout.CENTER);
        outPanel.add(styleChooser, BorderLayout.SOUTH);

        // 内容区域输入
        final JPanel inputPanel = new JPanel(new BorderLayout(0, 0));
        inputPanel.setOpaque(false);

        // 工具条
        final JPanel midToolBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 3, 3));
        midToolBar.setBackground(DEFAULT_MID_TOOLBAR_BACKGROUND);

        styleBtn = new JToggleButton(styleButtonIcon, false);
        styleBtn.setBorder(null);
        styleBtn.setContentAreaFilled(false);
        styleBtn.setFocusPainted(false);

        faceBtn = new JToggleButton(faceButtonIcon, false);
        faceBtn.setBorder(null);
        faceBtn.setContentAreaFilled(false);
        faceBtn.setFocusPainted(false);

        helpBtn = new JButton("?");
        helpBtn.setBorder(null);
//        helpBtn.setContentAreaFilled(false);
        helpBtn.setFocusPainted(false);
        helpBtn.setMargin(new Insets(10, 10, 10, 10));

        midToolBar.add(styleBtn);
        midToolBar.add(faceBtn);
        midToolBar.add(helpBtn);

        inputEditor = new RichEditor();

        inputPanel.add(midToolBar, BorderLayout.NORTH);
        inputPanel.add(inputEditor, BorderLayout.CENTER);

        contentPane.add(outPanel, JSplitPane.TOP);
        contentPane.add(inputPanel, JSplitPane.BOTTOM);

        this.add(avatarPanel, BorderLayout.NORTH);
        this.add(contentPane, BorderLayout.CENTER);
        this.setBackground(new Color(0xF4F9FC));

        faceChooser = new FaceChooser();

        handler = new Handler();

        styleBtn.addItemListener(handler);
        faceBtn.addItemListener(handler);
        helpBtn.addActionListener(handler);
        styleChooser.addPropertyChangeListener(handler);
        faceChooser.addActionListener(handler);
        inputEditor.getTextPane().addKeyListener(handler);

        this.setPreferredSize(new Dimension(400, 300));
    }

    @Override
    public Buddy getBuddy() {
        return this.buddy;
    }

    @Override
    public void writeContactMsg(ContactMsg msg) {
        onMsg(msg);
    }

    /**
     * 发送 / 接收消息处理------------------------------------------------------------------
     */
    private void sendMsg() {
        List<RichElement> content = inputEditor.getContent();

        // 内容输出
        String sendTime = new SimpleDateFormat("hh:mm:ss a").format(new Date());
        new Message("自己 " + sendTime, content).appendTo(outputEditor);
        outputEditor.appendParagraphEnd();
        inputEditor.clearContent();

        // 内容发送
        final ReplyMsg msg = new ReplyMsg();
        MsgBody msgBody = convertElements(content, inputEditor.getDefaultStyle());
        msg.setTo(buddy.getId());
        msg.setMsgBody(msgBody);

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                EventBusHolder.getEventBus().post(msg);
            }
        });
    }

    /**
     * 接收消息
     *
     * @param msg
     */
    public synchronized void onMsg(ContactMsg msg) {
        if (msg == null) {
            return;
        }

        String name = buddy.getNick();
        String mark = buddy.getMark();
        if (mark != null && mark.length() > 0) {
            name = mark;
        }

        String header = name + " " + new SimpleDateFormat("hh:mm:ss a").format(new Date(msg.getTime() * 1000));
        MsgBody msgBody = msg.getMsgBody();
        Iterable<RichElement> richElements = convertElements(msgBody, msgBody.getMsgStyle());
        new Message(header, richElements, Color.BLUE).appendTo(outputEditor);
        outputEditor.appendParagraphEnd();
    }

    public synchronized void msg(Iterable<ContactMsg> msgs) {
        if (msgs == null) {
            return;
        }
        for (ContactMsg msg : msgs) {
            onMsg(msg);
        }
    }

    /**
     * 请求 Input 获取输入焦点
     */
    public void requestFocusForInput() {
        inputEditor.getTextPane().requestFocus();
    }


    protected void setHelpText(String text) {
        this.helpText = text;
    }

    /**
     * 事件处理 --------------------------------------------------------------------
     */
    protected class Handler extends KeyAdapter implements PropertyChangeListener, ActionListener, ItemListener {
        // 快捷键
        @Override
        public void keyPressed(KeyEvent e) {
            // 如果不是 ALT + S
            if (!(e.isAltDown())) {
                return;
            }

            if (KeyEvent.VK_A == e.getKeyCode()) {
                styleBtn.setSelected(!styleBtn.isSelected());
                return;
            }

            if (!(e.isAltDown() && KeyEvent.VK_S == e.getKeyCode())) {
                return;
            }

            sendMsg();
        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyReleased(KeyEvent e) {
        }

        /**
         * 样式 / 表情 选择按钮处理
         */
        @Override
        public void itemStateChanged(ItemEvent e) {
            if (styleBtn == e.getSource()) {
                styleChooser.setVisible(ItemEvent.SELECTED == e.getStateChange());
                return;
            }

            if (faceBtn == e.getSource()) {
                if (ItemEvent.SELECTED == e.getStateChange()) {
                    Point f = faceBtn.getParent().getLocationOnScreen();
                    int width = faceChooser.getWidth();
                    int height = faceChooser.getHeight();

                    faceChooser.setLocation(f.x - width / 2, f.y - height);
                    faceChooser.setVisible(true);
                } else {
                    faceChooser.setVisible(false);
                }
            }
        }

        /**
         * 样式选择处理
         */
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            String prop = evt.getPropertyName();
            Object newValue = evt.getNewValue();
            Style style = inputEditor.getDefaultStyle();

            if (StyleChooser.FONT_FAMILY_PROPERTY_NAME.equals(prop)) {
                style.setFontFamily(String.valueOf(newValue));
                return;
            }

            if (StyleChooser.FONT_SIZE_PROPERTY_NAME.equals(prop)) {
                style.setFontSize(((Integer) newValue));
                return;
            }

            if (StyleChooser.BOLD_PROPERTY_NAME.equals(prop)) {
                style.setBold((Boolean) newValue);
                return;
            }

            if (StyleChooser.ITALIC_PROPERTY_NAME.equals(prop)) {
                style.setItalic((Boolean) newValue);
                return;
            }

            if (StyleChooser.UNDERLINE_PROPERTY_NAME.equals(prop)) {
                style.setUnderline((Boolean) newValue);
                return;
            }

            if (StyleChooser.COLOR_PROPERTY_NAME.equals(prop)) {
                style.setColor((Color) newValue);
            }
        }

        /**
         * 表情选择处理
         *
         * @param e
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            if (helpBtn == e.getSource()) {
                JOptionPane.showMessageDialog(null, helpText, "Help", JOptionPane.NO_OPTION);
                return;
            }

            if (FaceChooser.OK_COMMOND == e.getActionCommand()) {
                JLabel label = (JLabel) e.getSource();
                int id = Integer.parseInt(label.getToolTipText().replaceAll("[^0-9]", ""));
                new Face(id, label.getIcon()).appendTo(inputEditor);
                faceBtn.setSelected(false);
                requestFocusForInput();
                return;
            }

            /*-
             * 这里防止循环调用, 因为在鼠标点击 FaceChooser 外本来就会触发 CANCEL_COMMAND, 而 JToggleButton 自身也会触发
             * 结果调用两次 faceBtn.setSelected(false), 由于 JToggleButton 采用 setSelected(!isSelected()) 因此会出错
             */
            if (FaceChooser.CANCEL_COMMOND == e.getActionCommand() && e.getSource() != faceBtn) {
                faceBtn.setSelected(false);
                return;
            }
        }
    }

    /**
     * 转换函数 -----------------------------------------
     */
    Iterable<RichElement> convertElements(Iterable<MsgElement> msgElements, MsgStyle msgStyle) {
        Style style = convertStyle(msgStyle);
        List<RichElement> elements = new ArrayList<RichElement>();
        for (MsgElement me : msgElements) {
            AbstractElement element = convertElement(me);
            element.setStyle(style);
            elements.add(element);
        }
        return elements;
    }

    AbstractElement convertElement(MsgElement msgElement) {
        AbstractElement element = new Text(msgElement.toString());
        if (msgElement instanceof MsgElement.FaceElement) {
            int faceId = ((MsgElement.FaceElement) msgElement).getId();
            element = new Face(faceId, FaceUtil.faceID2DynamicFace(faceId));
        }

        return element;
    }

    MsgBody convertElements(Iterable<RichElement> elements, Style style) {
        MsgBody msgBody = new MsgBody();
        msgBody.setMsgStyle(convertStyle(style));
        for (RichElement e : elements) {
            msgBody.addElement(convertElement(e));
        }
        return msgBody;
    }

    MsgElement convertElement(RichElement element) {
        MsgElement me = MsgElement.createTextElement(element.toString());
        if (element instanceof Face) {
            me = MsgElement.createFaceElement(((Face) element).getId());
        }
        return me;
    }

    // 消息样式 --> JTextPane 样式
    Style convertStyle(MsgStyle msgStyle) {
        return new Style(msgStyle.getFontFamily(), msgStyle.getFontSize() + FONT_OFFSET, msgStyle.isBold(), msgStyle.isItalic(), msgStyle.isUnderline(), msgStyle.getColor());
    }

    MsgStyle convertStyle(Style style) {
        return new MsgStyle(style.getFontFamily(), style.getFontSize() - FONT_OFFSET, style.isBold(), style.isItalic(), style.isUnderline(), style.getColor());
    }
}
